

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/fluid.png">
  <link rel="icon" href="/img/favicon.ico">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="xuehaoweng">
  <meta name="keywords" content="">
  
    <meta name="description" content="快速开始Flask123456789from flask import Flaskapp &#x3D; Flask(__name__)@app.route(&quot;&#x2F;&quot;)def hello_world():    return &quot;&lt;p&gt;Hello, World!&lt;&#x2F;p&gt;&quot;  那么，这段代码做了什么？  首先我们进口了 Flask 班级。这个类的一个实">
<meta property="og:type" content="article">
<meta property="og:title" content="Flask 快速开始">
<meta property="og:url" content="http://blog.xhweng.cn/2024/01/05/Flask%20%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/index.html">
<meta property="og:site_name" content="有追求的开发者">
<meta property="og:description" content="快速开始Flask123456789from flask import Flaskapp &#x3D; Flask(__name__)@app.route(&quot;&#x2F;&quot;)def hello_world():    return &quot;&lt;p&gt;Hello, World!&lt;&#x2F;p&gt;&quot;  那么，这段代码做了什么？  首先我们进口了 Flask 班级。这个类的一个实">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://rregf9gjs.hd-bkt.clouddn.com/Snipaste_2023-03-18_16-47-59.png">
<meta property="og:image" content="http://rregf9gjs.hd-bkt.clouddn.com/Snipaste_2023-03-18_16-52-50.png">
<meta property="og:image" content="https://www.osgeo.cn/flask/_images/debugger.png">
<meta property="article:published_time" content="2024-01-05T01:26:02.000Z">
<meta property="article:modified_time" content="2024-03-27T02:51:50.435Z">
<meta property="article:author" content="xuehaoweng">
<meta property="article:tag" content="框架">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="http://rregf9gjs.hd-bkt.clouddn.com/Snipaste_2023-03-18_16-47-59.png">
  
  
    <meta name="referrer" content="no-referrer-when-downgrade">
  
  
  <title>Flask 快速开始 - 有追求的开发者</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/css/highlight-dark.css" />
  




  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"blog.xhweng.cn","root":"/","version":"1.9.7","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":true,"follow_dnt":true,"baidu":"b3bbb822b3d2a5e2761107c03cf72b8c","google":{"measurement_id":null},"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/local-search.xml","include_content_in_search":true};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
  

  
    <!-- Baidu Analytics -->
    <script async>
      if (!Fluid.ctx.dnt) {
        var _hmt = _hmt || [];
        (function() {
          var hm = document.createElement("script");
          hm.src = "https://hm.baidu.com/hm.js?b3bbb822b3d2a5e2761107c03cf72b8c";
          var s = document.getElementsByTagName("script")[0];
          s.parentNode.insertBefore(hm, s);
        })();
      }
    </script>
  

  
    <!-- Google tag (gtag.js) -->
    <script async>
      if (!Fluid.ctx.dnt) {
        Fluid.utils.createScript("https://www.googletagmanager.com/gtag/js?id=", function() {
          window.dataLayer = window.dataLayer || [];
          function gtag() {
            dataLayer.push(arguments);
          }
          gtag('js', new Date());
          gtag('config', '');
        });
      }
    </script>
  

  

  

  

  



  
<meta name="generator" content="Hexo 7.0.0"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>有追求的开发者</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/" target="_self">
                <i class="iconfont icon-home-fill"></i>
                <span>首页</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/" target="_self">
                <i class="iconfont icon-archive-fill"></i>
                <span>归档</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/" target="_self">
                <i class="iconfont icon-category-fill"></i>
                <span>分类</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/" target="_self">
                <i class="iconfont icon-tags-fill"></i>
                <span>标签</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/" target="_self">
                <i class="iconfont icon-user-fill"></i>
                <span>关于</span>
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              <i class="iconfont icon-search"></i>
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">
              <i class="iconfont icon-dark" id="color-toggle-icon"></i>
            </a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/img/default.png') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="Flask 快速开始"></span>
          
        </div>

        
          
  <div class="mt-3">
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2024-01-05 09:26" pubdate>
          2024年1月5日 上午
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          5.5k 字
        
      </span>
    

    
      <span class="post-meta mr-2">
        <i class="iconfont icon-clock-fill"></i>
        
        
        
          46 分钟
        
      </span>
    

    
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <h1 id="seo-header">Flask 快速开始</h1>
            
            
              <div class="markdown-body">
                
                <h1 id="快速开始Flask"><a href="#快速开始Flask" class="headerlink" title="快速开始Flask"></a>快速开始Flask</h1><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask<br><br>app = Flask(__name__)<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&quot;/&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">hello_world</span>():<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;&lt;p&gt;Hello, World!&lt;/p&gt;&quot;</span><br><br></code></pre></td></tr></table></figure>

<p>那么，这段代码做了什么？</p>
<ol>
<li><p>首先我们进口了 Flask 班级。这个类的一个实例就是我们的WSGI应用程序。</p>
</li>
<li><p>接下来我们创建这个类的一个实例。第一个参数是应用程序的模块或包的名称。 <strong>name</strong> 是一种方便快捷的方法，适用于大多数情况。这是必需的，这样Flask就知道在哪里可以找到模板和静态文件等资源。</p>
</li>
<li><p>然后我们使用 route() 装饰器来告诉 Flask 什么样的 URL可以触发函数 。</p>
</li>
<li><p>函数返回我们希望在用户浏览器中显示的消息。默认的内容类型是HTML，因此字符串中的HTML将由浏览器呈现。</p>
</li>
</ol>
<p><strong>注意</strong> ：另存为 hello.py 或者类似的东西。确保不要调用您的应用程序 flask.py 因为这会和flask库本身发生冲突。</p>
<p>要运行应用程序，请使用 flask 命令或 python -m flask . 在执行此操作之前，您需要通过导出 FLASK_APP 环境变量：</p>
<p>CMD：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs bash"><br><span class="hljs-built_in">export</span> FLASK_APP=hello.py<br>flask run<br>即可在默认5000端口访问<br> * Running on http://127.0.0.1:5000/<br><br><br></code></pre></td></tr></table></figure>

<p>类似下图:</p>
<p><img src="http://rregf9gjs.hd-bkt.clouddn.com/Snipaste_2023-03-18_16-47-59.png" srcset="/img/loading.gif" lazyload></p>
<p>如果想外部访问则需要指定ip</p>
<p><code>flask run --host=0.0.0.0</code></p>
<p><img src="http://rregf9gjs.hd-bkt.clouddn.com/Snipaste_2023-03-18_16-52-50.png" srcset="/img/loading.gif" lazyload></p>
<p>这将启动一个非常简单的内置服务器，这对于测试来说已经足够好了，但可能不是您想要在生产中使用的服务器。有关部署选项 <a target="_blank" rel="noopener" href="https://www.osgeo.cn/flask/deploying/index.html">常见Flask项目部署选择</a></p>
<h2 id="如果服务器不能启动，该怎么办"><a href="#如果服务器不能启动，该怎么办" class="headerlink" title="如果服务器不能启动，该怎么办"></a>如果服务器不能启动，该怎么办</h2><p>万一 python -m flask 失败或 flask 不存在，有多种原因可能是这种情况。首先，您需要查看错误消息。</p>
<h2 id="旧版Flask"><a href="#旧版Flask" class="headerlink" title="旧版Flask"></a>旧版Flask</h2><p>早于0.11的Flask版本以前有不同的方法来启动应用程序。简而言之 flask 命令不存在，也不存在 python -m flask . 在这种情况下，你有两个选择：要么升级到更新的Flask版本，要么看看 开发服务器 查看运行服务器的替代方法。</p>
<h2 id="导入名称无效"><a href="#导入名称无效" class="headerlink" title="导入名称无效"></a>导入名称无效</h2><p>这个 FLASK_APP 环境变量是要导入的模块的名称, 运行 flask run 命令就会导入这个模块。 如果该模块的名称不正确，您将在启动时收到导入错误。如果调试模式打开的情况下，会在运行到应用开始的时候出现导入错误。出错信息会告诉你尝试导入哪个模块时出错，为什么会出错。</p>
<p>最常见的原因是打字错误，或者因为你没有真正创建一个 app 对象。</p>
<h2 id="调试模式"><a href="#调试模式" class="headerlink" title="调试模式"></a>调试模式</h2><p>这个 flask run 命令不仅仅可以启动开发服务器。通过启用调试模式，如果代码发生更改，服务器将自动重新加载，如果请求期间发生错误，服务器将在浏览器中显示交互式调试器。</p>
<p><img src="https://www.osgeo.cn/flask/_images/debugger.png" srcset="/img/loading.gif" lazyload></p>
<h2 id="HTML逃逸"><a href="#HTML逃逸" class="headerlink" title="HTML逃逸"></a>HTML逃逸</h2><p>当返回HTML（Flask中的默认响应类型）时，必须对输出中呈现的任何用户提供的值进行转义，以防止注入攻击。用Jinja呈现的HTML模板（稍后介绍）将自动执行此操作。</p>
<p>escape() ，可以手动使用。为了简洁起见，在大多数示例中都省略了它，但是您应该始终了解如何使用不可信的数据。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">from</span> markupsafe <span class="hljs-keyword">import</span> escape<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&quot;/&lt;name&gt;&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">hello</span>(<span class="hljs-params">name</span>):<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">f&quot;Hello, <span class="hljs-subst">&#123;escape(name)&#125;</span>!&quot;</span><br><br></code></pre></td></tr></table></figure>

<p>如果用户成功提交了名称 <code>&lt;script&gt;alert(&quot;bad&quot;)&lt;/script&gt;</code> ，转义使其呈现为文本，而不是在用户浏览器中运行脚本。<br><name> 在路由中，从URL捕获一个值并将其传递给view函数。这些变量规则解释如下。</p>
<h2 id="路由"><a href="#路由" class="headerlink" title="路由"></a>路由</h2><p>现代Web应用都使用有意义的URL，这样有易于人们辨识记忆。用户都喜欢可以不访问索引页就可以直接访问想要的那个页面。</p>
<p>使用 route() 装饰器来把函数绑定到对应的 URL:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;Index Page&#x27;</span><br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/hello&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">hello</span>():<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;Hello, World&#x27;</span><br></code></pre></td></tr></table></figure>

<p>但是能做的不仅仅是这些！你可以使URL的某些部分成为动态的，并将多个规则附加到一个函数。</p>
<h2 id="变量规则"><a href="#变量规则" class="headerlink" title="变量规则"></a>变量规则</h2><p>通过把 URL 的一部分标记为 <variable_name> 就可以在 URL 中添加变量。标记的部分会作为关键字参数传递给函数。通过使用 <a href="converter:variable_name">converter:variable_name</a> ，可以选择性的加上一个转换器，为变量指定规则。请看下面的例子:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">from</span> markupsafe <span class="hljs-keyword">import</span> escape<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/user/&lt;username&gt;&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">show_user_profile</span>(<span class="hljs-params">username</span>):<br>    <span class="hljs-comment"># show the user profile for that user</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-string">f&#x27;User <span class="hljs-subst">&#123;username&#125;</span>&#x27;</span><br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/post/&lt;int:post_id&gt;&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">show_post</span>(<span class="hljs-params">post_id</span>):<br>    <span class="hljs-comment"># show the post with the given id, the id is an integer</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-string">f&#x27;Post <span class="hljs-subst">&#123;post_id&#125;</span>&#x27;</span><br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/path/&lt;path:subpath&gt;&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">show_subpath</span>(<span class="hljs-params">subpath</span>):<br>    <span class="hljs-comment"># show the subpath after /path/</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-string">f&#x27;Subpath <span class="hljs-subst">&#123;subpath&#125;</span>&#x27;</span><br><br></code></pre></td></tr></table></figure>

<h2 id="唯一的URL-重定向行为"><a href="#唯一的URL-重定向行为" class="headerlink" title="唯一的URL&#x2F;重定向行为"></a>唯一的URL&#x2F;重定向行为</h2><p>以下两条规则的不同之处在于是否使用尾部的斜杠。:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/projects/&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">projects</span>():<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;The project page&#x27;</span><br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/about&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">about</span>():<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;The about page&#x27;</span><br></code></pre></td></tr></table></figure>

<h2 id="URL构建"><a href="#URL构建" class="headerlink" title="URL构建"></a>URL构建</h2><p>url_for() 函数用于构建指定函数的 URL。它把函数名称作为第一个参数。它可以接受任意个关键字参数，每个关键字参数对应 URL 中的变量。未知变量 将添加到 URL 中作为查询参数。</p>
<p>为什么要使用URL反转函数生成URL，而非在模板中硬编码？</p>
<ul>
<li><p>反转通常比硬编码URL更具描述性。</p>
</li>
<li><p>你可以只在一个地方改变 URL ，而不用到处乱找。</p>
</li>
<li><p>URL构建透明地处理特殊字符的转义。</p>
</li>
<li><p>生成的路径总是绝对的，可以避免相对路径产生副作用。</p>
</li>
<li><p>如果你的应用是放在 URL 根路径之外的地方（如在 &#x2F;myapplication 中，不在 &#x2F; 中）， url_for() 会为你妥善处理</p>
</li>
</ul>
<p>例如，这里我们使用 test_request_context() 方法来尝试使用 url_for() 。 test_request_context() 告诉 Flask 正在处理一个请求，而实际上也许我们正处在交互 Python shell 之中， 并没有真正的请求。参见 本地环境 。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> url_for<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;index&#x27;</span><br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/login&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">login</span>():<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;login&#x27;</span><br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/user/&lt;username&gt;&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">profile</span>(<span class="hljs-params">username</span>):<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">f&#x27;<span class="hljs-subst">&#123;username&#125;</span>\&#x27;s profile&#x27;</span><br><br><span class="hljs-keyword">with</span> app.test_request_context():<br>    <span class="hljs-built_in">print</span>(url_for(<span class="hljs-string">&#x27;index&#x27;</span>))<br>    <span class="hljs-built_in">print</span>(url_for(<span class="hljs-string">&#x27;login&#x27;</span>))<br>    <span class="hljs-built_in">print</span>(url_for(<span class="hljs-string">&#x27;login&#x27;</span>, <span class="hljs-built_in">next</span>=<span class="hljs-string">&#x27;/&#x27;</span>))<br>    <span class="hljs-built_in">print</span>(url_for(<span class="hljs-string">&#x27;profile&#x27;</span>, username=<span class="hljs-string">&#x27;John Doe&#x27;</span>))<br><br></code></pre></td></tr></table></figure>


<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python">/<br>/login<br>/login?<span class="hljs-built_in">next</span>=/<br>/user/John%20Doe<br><br></code></pre></td></tr></table></figure>

<h2 id="HTTP方法"><a href="#HTTP方法" class="headerlink" title="HTTP方法"></a>HTTP方法</h2><p>Web应用程序在访问URL时使用不同的HTTP方法。在使用flask时，应该熟悉HTTP方法。默认情况下，一个路由只回应 GET 请求。可以使用 route() 装饰器的 methods 参数来处理不同的 HTTP 方法:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> request<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/login&#x27;</span>, methods=[<span class="hljs-string">&#x27;GET&#x27;</span>, <span class="hljs-string">&#x27;POST&#x27;</span>]</span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">login</span>():<br>    <span class="hljs-keyword">if</span> request.method == <span class="hljs-string">&#x27;POST&#x27;</span>:<br>        <span class="hljs-keyword">return</span> do_the_login()<br>    <span class="hljs-keyword">else</span>:<br>        <span class="hljs-keyword">return</span> show_the_login_form()<br><br></code></pre></td></tr></table></figure>
<p>如果当前使用了 GET 方法， Flask 会自动添加 HEAD 方法支持，并且同时还会 按照 HTTP RFC 来处理 HEAD 请求。同样， OPTIONS 也会自动实现。</p>
<h2 id="静态文件"><a href="#静态文件" class="headerlink" title="静态文件"></a>静态文件</h2><p>动态Web应用程序还需要静态文件。通常是css和javascript文件。理想情况下，你的Web服务器已经配置好了为你提供静态文件服务，但在开发期间，Flask也可以这样做。只要在你的包或模块旁边创建一个名为 static 的文件夹就行了。 静态文件位于应用的 &#x2F;static 中。</p>
<p>使用特定的 ‘static’ 端点就可以生成相应的 URL</p>
<p><code>url_for(&#39;static&#39;, filename=&#39;style.css&#39;)</code></p>
<p>这个静态文件在文件系统中的位置应该是 static&#x2F;style.css 。</p>
<h2 id="渲染模板"><a href="#渲染模板" class="headerlink" title="渲染模板"></a>渲染模板</h2><p>从Python中生成HTML并不有趣，实际上相当麻烦，因为您必须自己进行HTML转义以保证应用程序的安全。因为这个瓶子配置了 Jinja2 自动为您提供模板引擎。</p>
<p>使用 render_template() 方法可以渲染模板，你只要提供模板名称和需要 作为参数传递给模板的变量就行了。下面是一个简单的模板渲染例子:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> render_template<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/hello/&#x27;</span></span>)</span><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/hello/&lt;name&gt;&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">hello</span>(<span class="hljs-params">name=<span class="hljs-literal">None</span></span>):<br>    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">&#x27;hello.html&#x27;</span>, name=name)<br></code></pre></td></tr></table></figure>

<p>Flask 会在 templates 文件夹内寻找模板。因此，如果你的应用是一个模块， 那么模板文件夹应该在模块旁边；如果是一个包，那么就应该在包里面：</p>
<p>情形1: 一个模板：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python"><br>/application.py<br>/templates<br>    /hello.html<br><br></code></pre></td></tr></table></figure>

<p>情形2: 一个包：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python"><br>/application<br>    /__init__.py<br>    /templates<br>        /hello.html<br></code></pre></td></tr></table></figure>

<p>对于模板，您可以使用jinja2模板的全部功能。去见官员 Jinja2 Template Documentation 更多信息。</p>
<p>下面是一个示例模板：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs html"><br><span class="hljs-meta">&lt;!doctype <span class="hljs-keyword">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Hello from Flask<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br>&#123;% if name %&#125;<br>  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello &#123;&#123; name &#125;&#125;!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><br>&#123;% else %&#125;<br>  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello, World!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><br>&#123;% endif %&#125;<br><br></code></pre></td></tr></table></figure>
<p>在模板内部可以和访问 get_flashed_messages() 函数一样访问 request 、 session 和 g [1] 对象。</p>
<p>如果使用继承，模板尤其有用。如果你想知道这是怎么回事，看 模板继承 . 基本上，模板继承可以在每个页面上保留某些元素（如页眉、导航和页脚）。</p>
<p>自动转义已启用，因此如果 name 包含HTML，将自动转义。如果您可以信任一个变量，并且知道它将是安全的HTML（例如，因为它来自一个将wiki标记转换为HTML的模块），则可以使用 Markup 类或通过使用 |safe 在模板中筛选。有关更多示例，请参阅Jinja 2文档。</p>
<h2 id="访问请求数据"><a href="#访问请求数据" class="headerlink" title="访问请求数据"></a>访问请求数据</h2><p>对于 web 应用来说对客户端向服务器发送的数据作出响应很重要。在 Flask 中由全局 对象 request 来提供请求信息。如果你有一些 Python 基础，那么 可能会奇怪：既然这个对象是全局的，怎么还能保持线程安全？答案是本地环境：</p>
<h2 id="环境局部变量"><a href="#环境局部变量" class="headerlink" title="环境局部变量"></a>环境局部变量</h2><!-- 内幕消息
如果你想了解工作原理和如何使用局部环境进行测试，那么请阅读本节， 否则可以跳过本节。 -->

<p>某些对象在 Flask 中是全局对象，但不是通常意义下的全局对象。这些对象实际上是 特定环境下局部对象的代理。虽然很拗口，但还是很容易理解的。</p>
<p>设想现在处于处理线程的环境中。一个请求传入，服务器决定生成一个新线程（或者别的什么东西，这个底层的东西能够处理包括线程在内的并发系统）。当 Flask 开始其内部请求处理时会把当前线程作为活动环境，并把当前应用和 WSGI 环境绑定到这个环境（线程）上。它以一种聪明的方式使得一个应用可以在不中断的情况下调用另一个应用上。</p>
<p>这对你意味着什么？基本上你可以完全不必理会。这个只有在做类似单元测试时才有用。在测试时会遇到由于没有请求对象而导致依赖于请求的代码会突然崩溃的情况。解决方案是自己创建一个请求对象并绑定到环境。最简单的单元测试解决方案是使用 test_request_context() 环境管理器。通过使用 with 声明可以绑定一个测试请求，以便于交互。例如:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> request<br><br><span class="hljs-keyword">with</span> app.test_request_context(<span class="hljs-string">&#x27;/hello&#x27;</span>, method=<span class="hljs-string">&#x27;POST&#x27;</span>):<br>    <span class="hljs-comment"># now you can do something with the request until the</span><br>    <span class="hljs-comment"># end of the with block, such as basic assertions:</span><br>    <span class="hljs-keyword">assert</span> request.path == <span class="hljs-string">&#x27;/hello&#x27;</span><br>    <span class="hljs-keyword">assert</span> request.method == <span class="hljs-string">&#x27;POST&#x27;</span><br></code></pre></td></tr></table></figure>

<p>另一种可能性是将整个WSGi环境传递给 request_context() 方法：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">with</span> app.request_context(environ):<br>    <span class="hljs-keyword">assert</span> request.method == <span class="hljs-string">&#x27;POST&#x27;</span><br></code></pre></td></tr></table></figure>

<p>请求对象<br>API章节对请求对象做了详尽阐述（参见request)，因此这里不会赘述。此处宽泛介绍一些最常用的操作。首先从Flask模块里导入它：</p>
<p><code>from flask import request</code><br>通过使用 method 属性可以操作当前请求方法，通过使用 form 属性处理表单数据（在 POST 或者 PUT 请求中传输的数据）。以下是使用上述两个属性的例子:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/login&#x27;</span>, methods=[<span class="hljs-string">&#x27;POST&#x27;</span>, <span class="hljs-string">&#x27;GET&#x27;</span>]</span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">login</span>():<br>    error = <span class="hljs-literal">None</span><br>    <span class="hljs-keyword">if</span> request.method == <span class="hljs-string">&#x27;POST&#x27;</span>:<br>        <span class="hljs-keyword">if</span> valid_login(request.form[<span class="hljs-string">&#x27;username&#x27;</span>],<br>                       request.form[<span class="hljs-string">&#x27;password&#x27;</span>]):<br>            <span class="hljs-keyword">return</span> log_the_user_in(request.form[<span class="hljs-string">&#x27;username&#x27;</span>])<br>        <span class="hljs-keyword">else</span>:<br>            error = <span class="hljs-string">&#x27;Invalid username/password&#x27;</span><br>    <span class="hljs-comment"># the code below is executed if the request method</span><br>    <span class="hljs-comment"># was GET or the credentials were invalid</span><br>    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">&#x27;login.html&#x27;</span>, error=error)<br></code></pre></td></tr></table></figure>
<p>当 form 属性中不存在这个键时会发生什么？会引发一个 KeyError 的异常。 如果你不像捕捉一个标准错误一样捕捉 KeyError，那么会显示一个 HTTP 400 Bad Request 错误页面。因此，多数情况下你不必处理这个问题。</p>
<p>你可以使用args 属性来访问在URL中提交的参数（ ?key&#x3D;value ）：</p>
<p><code>searchword = request.args.get(&#39;key&#39;, &#39;&#39;)</code></p>
<p>我们建议使用 get 或通过捕捉 KeyError 来访问 URL 参数。因为在这种情况下，用户可能会更改URL导致一个400错误的请求页面，这样会影响用户体验。</p>
<p>要获得请求对象的方法和属性的完整列表，请参阅Request文档。</p>
<h2 id="文件上传"><a href="#文件上传" class="headerlink" title="文件上传"></a>文件上传</h2><p>你可以用flask轻松处理上传的文件。只要确保不要忘记在HTM里设置 enctype&#x3D;”multipart&#x2F;form-data” 属性，否则浏览器根本不会发送文件。</p>
<p>上载的文件存储在内存中或文件系统上的临时位置。您可以通过查看 files 请求对象的属性。每个上载的文件都存储在该字典中。它的行为就像一条标准的 Python file 对象，但它也有一个 save() 方法，该方法允许您将该文件存储在服务器的文件系统中。下面是一个简单的例子，展示了它的工作原理：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> request<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/upload&#x27;</span>, methods=[<span class="hljs-string">&#x27;GET&#x27;</span>, <span class="hljs-string">&#x27;POST&#x27;</span>]</span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">upload_file</span>():<br>    <span class="hljs-keyword">if</span> request.method == <span class="hljs-string">&#x27;POST&#x27;</span>:<br>        f = request.files[<span class="hljs-string">&#x27;the_file&#x27;</span>]<br>        f.save(<span class="hljs-string">&#x27;/var/www/uploads/uploaded_file.txt&#x27;</span>)<br>    ...<br><br></code></pre></td></tr></table></figure>
<p>如果您想知道文件在上传到应用程序之前是如何在客户机上命名的，可以访问 filename 属性。但是请记住，这个价值观是可以伪造的，所以永远不要相信这个价值观。如果要使用客户端的文件名在服务器上存储文件，请通过 secure_filename() Werkzeug为您提供的功能：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> werkzeug.utils <span class="hljs-keyword">import</span> secure_filename<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/upload&#x27;</span>, methods=[<span class="hljs-string">&#x27;GET&#x27;</span>, <span class="hljs-string">&#x27;POST&#x27;</span>]</span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">upload_file</span>():<br>    <span class="hljs-keyword">if</span> request.method == <span class="hljs-string">&#x27;POST&#x27;</span>:<br>        file = request.files[<span class="hljs-string">&#x27;the_file&#x27;</span>]<br>        file.save(<span class="hljs-string">f&quot;/var/www/uploads/<span class="hljs-subst">&#123;secure_filename(f.filename)&#125;</span>&quot;</span>)<br>    ...<br></code></pre></td></tr></table></figure>
<p>有关更好的示例，请参见 上传文件 .</p>
<h2 id="Cookies"><a href="#Cookies" class="headerlink" title="Cookies"></a>Cookies</h2><p>可以使用 cookies 属性来访问 cookies 。可以使用响应对象的 set_cookie 方法来设置 cookies 。请求对象的 cookies 属性是一个包含了客户端传输的所有 cookies 的字典。在 Flask 中，如果使用会话，那么就不要直接使用 cookies ，请参考会话一节。</p>
<p><strong>读取cookie</strong>:：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> request<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():<br>    username = request.cookies.get(<span class="hljs-string">&#x27;username&#x27;</span>)<br>    <span class="hljs-comment"># use cookies.get(key) instead of cookies[key] to not get a</span><br>    <span class="hljs-comment"># KeyError if the cookie is missing.</span><br></code></pre></td></tr></table></figure>
<p><strong>存储cookie</strong>:：</p>
<p>from flask import make_response</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():<br>    resp = make_response(render_template(...))<br>    resp.set_cookie(<span class="hljs-string">&#x27;username&#x27;</span>, <span class="hljs-string">&#x27;the username&#x27;</span>)<br>    <span class="hljs-keyword">return</span> resp<br></code></pre></td></tr></table></figure>
<p>请注意， cookies 是设置在响应对象上。通常只是从视图函数返回字符串，Flask 会把它们转换为响应对象。如果你想显式地转换，那么可以使用 make_response() 函数，然后再修改它。</p>
<p>有时，您可能希望将cookie设置在响应对象尚不存在的位置。这可以通过使用 延迟的请求回调 模式。</p>
<p>有关此信息，请参见 :about-responses。</p>
<h2 id="重定向和错误"><a href="#重定向和错误" class="headerlink" title="重定向和错误"></a>重定向和错误</h2><p>使用 redirect() 函数可以重定向。使用 abort() 可以更早退出请求，并返回错误代码:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> abort, redirect, url_for<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():<br>    <span class="hljs-keyword">return</span> redirect(url_for(<span class="hljs-string">&#x27;login&#x27;</span>))<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/login&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">login</span>():<br>    abort(<span class="hljs-number">401</span>)<br>    this_is_never_executed()<br></code></pre></td></tr></table></figure>

<p>这是一个相当无意义的例子，它让一个用户从索引页重定向到一个无法访问的页面（401 表示禁止访问）。但是上例可以说明重定向和出错跳出是如何工作的。</p>
<p>默认情况下，每个错误代码都会显示一个黑白错误页。如果要自定义错误页，可以使用 errorhandler装饰器：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs python"><br><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> render_template<br><br><span class="hljs-meta">@app.errorhandler(<span class="hljs-params"><span class="hljs-number">404</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">page_not_found</span>(<span class="hljs-params">error</span>):<br>    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">&#x27;page_not_found.html&#x27;</span>), <span class="hljs-number">404</span><br></code></pre></td></tr></table></figure>

<p>注意 render_template() 后面的 404 ，这表示页面对应的出错代码是 404 ，即页面不存在。默认为 200 表示：一切正常。</p>
<h2 id="关于响应"><a href="#关于响应" class="headerlink" title="关于响应"></a>关于响应</h2><p>视图函数的返回值将自动转换为响应对象。如果返回值是一个字符串，它将被转换为一个以字符串作为响应体的响应对象，则 200 OK 状态代码和A text&#x2F;html 模仿型。如果返回值是dict， jsonify() 被调用以产生响应。Flask应用于将返回值转换为响应对象的逻辑如下：</p>
<ul>
<li><p>如果返回的是一个合法的响应对象，它会从视图直接返回。</p>
</li>
<li><p>如果返回的是一个字符串，响应对象会用字串符数据和默认参数创建。</p>
</li>
<li><p>如果是dict，则使用 jsonify .</p>
</li>
<li><p>如果返回一个元组，元组中的项可以提供额外的信息。这样的元组必须是 (response, status) ， (response, headers) 或 (response, status, headers) . 这个 status 值将覆盖状态代码和 headers 可以是其他头值的列表或字典。</p>
</li>
<li><p>如果以上都不是，那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。</p>
</li>
</ul>
<p>如果要在视图中获取响应对象的结果，可以使用make_response函数。</p>
<p>比如你有这样一个视图：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> render_template<br><br><span class="hljs-meta">@app.errorhandler(<span class="hljs-params"><span class="hljs-number">404</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">not_found</span>(<span class="hljs-params">error</span>):<br>    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">&#x27;error.html&#x27;</span>), <span class="hljs-number">404</span><br><br></code></pre></td></tr></table></figure>
<p>你只需要把返回值表达式传递给make_response，获取响应对象并修改它，然后返回它：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> make_response<br><br><span class="hljs-meta">@app.errorhandler(<span class="hljs-params"><span class="hljs-number">404</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">not_found</span>(<span class="hljs-params">error</span>):<br>    resp = make_response(render_template(<span class="hljs-string">&#x27;error.html&#x27;</span>), <span class="hljs-number">404</span>)<br>    resp.headers[<span class="hljs-string">&#x27;X-Something&#x27;</span>] = <span class="hljs-string">&#x27;A value&#x27;</span><br>    <span class="hljs-keyword">return</span> resp<br><br></code></pre></td></tr></table></figure>

<h2 id="使用JSON的API"><a href="#使用JSON的API" class="headerlink" title="使用JSON的API"></a>使用JSON的API</h2><p>编写API时，一种常见的响应格式是JSON。开始用Flask编写这样一个API很容易。如果您返回 dict 从视图来看，它将被转换为JSON响应。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&quot;/me&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">me_api</span>():<br>    user = get_current_user()<br>    <span class="hljs-keyword">return</span> &#123;<br>        <span class="hljs-string">&quot;username&quot;</span>: user.username,<br>        <span class="hljs-string">&quot;theme&quot;</span>: user.theme,<br>        <span class="hljs-string">&quot;image&quot;</span>: url_for(<span class="hljs-string">&quot;user_image&quot;</span>, filename=user.image),<br>    &#125;<br></code></pre></td></tr></table></figure>
<p>根据您的API设计，您可能希望为以下类型创建JSON响应 dict . 在这种情况下，使用 jsonify() 函数，该函数将序列化任何受支持的JSON数据类型。或者看看支持更复杂应用程序的Flask社区扩展。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> jsonify<br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&quot;/users&quot;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">users_api</span>():<br>    users = get_all_users()<br>    <span class="hljs-keyword">return</span> jsonify([user.to_json() <span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> users])<br></code></pre></td></tr></table></figure>
<h2 id="会话¶"><a href="#会话¶" class="headerlink" title="会话¶"></a>会话¶</h2><p>除了请求对象，还有一个名为 session 的对象，允许你在不同请求之间储存信息。这个对象相当于用密钥签名加密的 cookie ，即用户可以查看你的 cookie ，但是如果没有密钥就无法修改它。</p>
<p>要使用会话，你必须设置一个密钥。以下是会话的工作方式：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> session<br><br><span class="hljs-comment"># Set the secret key to some random bytes. Keep this really secret!</span><br>app.secret_key = <span class="hljs-string">b&#x27;_5#y2L&quot;F4Q8z\n\xec]/&#x27;</span><br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">index</span>():<br>    <span class="hljs-keyword">if</span> <span class="hljs-string">&#x27;username&#x27;</span> <span class="hljs-keyword">in</span> session:<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">f&#x27;Logged in as <span class="hljs-subst">&#123;session[<span class="hljs-string">&quot;username&quot;</span>]&#125;</span>&#x27;</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;You are not logged in&#x27;</span><br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/login&#x27;</span>, methods=[<span class="hljs-string">&#x27;GET&#x27;</span>, <span class="hljs-string">&#x27;POST&#x27;</span>]</span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">login</span>():<br>    <span class="hljs-keyword">if</span> request.method == <span class="hljs-string">&#x27;POST&#x27;</span>:<br>        session[<span class="hljs-string">&#x27;username&#x27;</span>] = request.form[<span class="hljs-string">&#x27;username&#x27;</span>]<br>        <span class="hljs-keyword">return</span> redirect(url_for(<span class="hljs-string">&#x27;index&#x27;</span>))<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;&#x27;&#x27;</span><br><span class="hljs-string">        &lt;form method=&quot;post&quot;&gt;</span><br><span class="hljs-string">            &lt;p&gt;&lt;input type=text name=username&gt;</span><br><span class="hljs-string">            &lt;p&gt;&lt;input type=submit value=Login&gt;</span><br><span class="hljs-string">        &lt;/form&gt;</span><br><span class="hljs-string">    &#x27;&#x27;&#x27;</span><br><br><span class="hljs-meta">@app.route(<span class="hljs-params"><span class="hljs-string">&#x27;/logout&#x27;</span></span>)</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">logout</span>():<br>    <span class="hljs-comment"># remove the username from the session if it&#x27;s there</span><br>    session.pop(<span class="hljs-string">&#x27;username&#x27;</span>, <span class="hljs-literal">None</span>)<br>    <span class="hljs-keyword">return</span> redirect(url_for(<span class="hljs-string">&#x27;index&#x27;</span>))<br><br></code></pre></td></tr></table></figure>

<p>如何生成好的密钥<br>一个好的密钥应当有足够的随机性。 操作系统可以有多种方式基于密码随机生成器来生成随机数据。使用下面的命令可以快捷的为 Flask.secret_key （ 或者 SECRET_KEY ）生成值:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">python -c <span class="hljs-string">&#x27;import os; </span><br><span class="hljs-string">print(os.urandom(16))&#x27;</span><br>b<span class="hljs-string">&#x27;_5#y2L&quot;F4Q8z\n\xec]/&#x27;</span><br></code></pre></td></tr></table></figure>
<p>使用基于cookie的会话需要注意：Flask 会取出会话对象中的值，把值序列化后储存到 cookie 中。在打开 cookie 的情况下，如果需要查找某个值，但是这个值在请求中没有持续储存的话，那么不会得到一个明确的出错信息。请检查页面响应中的 cookie 的大小是否与网络浏览器所支持的大小一致。</p>
<p>除了默认的客户端的会话之外，还有许多 Flask 扩展支持服务端会话。</p>
<h2 id="信息闪现"><a href="#信息闪现" class="headerlink" title="信息闪现"></a>信息闪现</h2><p>好的应用程序和用户界面都有良好的反馈。如果用户没有得到足够的反馈，他们可能最终会讨厌这个应用程序。flask通过闪现系统提供了一种非常简单的向用户提供反馈的方式。闪现系统的基本工作原理是在请求结束时记录一个消息，提供且只提供给下一个请求使用。通常通过一个布局模板来展现闪现的消息。</p>
<p>要闪烁信息，请使用 flash() 方法，获取可以使用的消息 get_flashed_messages() 也可以在模板中找到。看到了吗 信息闪烁 举一个完整的例子。</p>
<h2 id="日志记录"><a href="#日志记录" class="headerlink" title="日志记录"></a>日志记录</h2><p>Changelog<br>有时候你会遇到这样的情况，你处理的数据本应该是正确的，但实际上不是。例如因为用户篡改了数据或客户端代码出错而导致一个客户端代码向服务器发送了明显错误的 HTTP 请求。多数时候在类似情况下，返回 400 Bad Request 就没事了，但是有时候不能这么做，并且要让代码继续运行。</p>
<p>你可能还想记录下发生了什么问题。这时候日志记录就派上了用场。从Flask0.3开始，就已经为你配置好了一个日志工具。</p>
<p>以下是一些调用日志纪录的示例：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">app.logger.debug(<span class="hljs-string">&#x27;A value for debugging&#x27;</span>)<br>app.logger.warning(<span class="hljs-string">&#x27;A warning occurred (%d apples)&#x27;</span>, 42)<br>app.logger.error(<span class="hljs-string">&#x27;An error occurred&#x27;</span>)<br></code></pre></td></tr></table></figure>

<p>附件 logger 是标准日志记录 Logger 所以直接去找官员 logging 有关详细信息，请参阅文档。</p>
<h2 id="钩子WSGI中间件"><a href="#钩子WSGI中间件" class="headerlink" title="钩子WSGI中间件"></a>钩子WSGI中间件</h2><p>要将WSGI中间件添加到Flask应用程序中，请包装应用程序的 wsgi_app 属性。例如，应用Werkzeug的 ProxyFix 运行在Nginx后面的中间件：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> werkzeug.middleware.proxy_fix <span class="hljs-keyword">import</span> ProxyFix<br>app.wsgi_app = ProxyFix(app.wsgi_app)<br></code></pre></td></tr></table></figure>
<p>包装 app.wsgi_app 而不是 app 意思是说 app 仍然指向Flask应用程序，而不是中间件，因此您可以继续使用和配置 app 直接。</p>
<h2 id="使用Flask扩展"><a href="#使用Flask扩展" class="headerlink" title="使用Flask扩展"></a>使用Flask扩展</h2><p>扩展是帮助完成公共任务的包。例如 Flask-SQLAlchemy 为在 Flask 中轻松使用 SQLAlchemy 提供支持。</p>
<p>有关flask扩展的更多信息，请参阅 <a target="_blank" rel="noopener" href="https://www.osgeo.cn/flask/extensions.html">扩展</a> .</p>
<h2 id="部署到Web服务器"><a href="#部署到Web服务器" class="headerlink" title="部署到Web服务器"></a>部署到Web服务器</h2><p>准备好部署你的新Flask应用了吗？看到了吗 <a target="_blank" rel="noopener" href="https://www.osgeo.cn/flask/deploying/index.html">部署选择</a>.</p>

                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/categories/%E6%A1%86%E6%9E%B6/" class="category-chain-item">框架</a>
  
  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/tags/%E6%A1%86%E6%9E%B6/" class="print-no-link">#框架</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>Flask 快速开始</div>
      <div>http://blog.xhweng.cn/2024/01/05/Flask 快速开始/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>xuehaoweng</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2024年1月5日</div>
        </div>
      
      
      
        <div class="license-meta-item">
          <div>许可协议</div>
          <div>
            
              
              
                <a class="print-no-link" target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
                  <span class="hint--top hint--rounded" aria-label="BY - 署名">
                    <i class="iconfont icon-by"></i>
                  </span>
                </a>
              
            
          </div>
        </div>
      
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2024/01/05/FastAPI%20%E7%AE%80%E4%BB%8B/" title="FastAPI 简介">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">FastAPI 简介</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2024/01/05/Flask%20%E5%AE%89%E8%A3%85/" title="Flask 安装">
                        <span class="hidden-mobile">Flask 安装</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="margin-left: -1rem">
    <div id="toc">
  <p class="toc-header">
    <i class="iconfont icon-list"></i>
    <span>目录</span>
  </p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
    </div>
  
  
    <div class="statistics">
  
  

  
    
      <span id="busuanzi_container_site_pv" style="display: none">
        总访问量 
        <span id="busuanzi_value_site_pv"></span>
         次
      </span>
    
    
      <span id="busuanzi_container_site_uv" style="display: none">
        总访客数 
        <span id="busuanzi_value_site_uv"></span>
         人
      </span>
    
    
  
</div>

  
  
    <!-- 备案信息 ICP for China -->
    <div class="beian">
  <span>
    <a href="http://beian.miit.gov.cn/" target="_blank" rel="nofollow noopener">
      皖ICP备2024038809号-2
    </a>
  </span>
  
    
      <span>
        <a
          href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=12345678"
          rel="nofollow noopener"
          class="beian-police"
          target="_blank"
        >
          
            <span style="visibility: hidden; width: 0">|</span>
            <img src="/img/police_beian.png" srcset="/img/loading.gif" lazyload alt="police-icon"/>
          
          <span>京公网安备12345678号</span>
        </a>
      </span>
    
  
</div>

  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.4/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.20.1/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init(Object.assign({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      scrollSmooth    : true,
      includeTitleTags: true,
      headingsOffset  : -boardTop,
    }, CONFIG.toc));
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }

    Fluid.events.registerRefreshCallback(function() {
      if ('tocbot' in window) {
        tocbot.refresh();
        var toc = jQuery('#toc');
        if (toc.length === 0 || !tocbot) {
          return;
        }
        if (toc.find('.toc-list-item').length > 0) {
          toc.css('visibility', 'visible');
        }
      }
    });
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.11/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));

    Fluid.events.registerRefreshCallback(function() {
      if ('anchors' in window) {
        anchors.removeAll();
        var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
        var res = [];
        for (var item of el) {
          res.push('.markdown-body > ' + item.trim());
        }
        if (CONFIG.anchorjs.placement === 'left') {
          anchors.options.class = 'anchorjs-link-left';
        }
        anchors.add(res.join(', '));
      }
    });
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/js/local-search.js" ></script>

  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>





<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
